use crate::configure;
use crate::handler::handler_404;
use crate::router::init_router;
use crate::utils::request_logger::request_logger_middleware;
use crate::{ApiDoc, configure::CONF};
use axum::Router;
use axum::middleware;
use axum::routing::get;
use log::{debug, info};
use tokio::net::TcpListener;
use tokio::signal;
use utoipa::OpenApi;
use utoipa_scalar::{Scalar, Servable};

pub async fn run() -> anyhow::Result<()> {
    configure::init().await;

    let app = Router::new()
        .merge(Scalar::with_url("/scalar", ApiDoc::openapi()))
        .fallback(handler_404)
        .nest("/api", init_router()?)
        .layer(middleware::from_fn(request_logger_middleware));

    debug!("debug app: {:#?}", &app);

    let host = CONF
        .get_string("server.host")
        .unwrap_or("0.0.0.0".to_string());
    let port = CONF.get_int("server.port").unwrap_or(8080);

    let addr = format!("{}:{}", host, port);
    debug!("Creating router and binding to {}", addr);

    let listener = TcpListener::bind(&addr).await?;
    info!("Server listening on {}", addr);
    info!("Swagger UI available at: http://{}/scalar", addr);

    axum::serve(listener, app)
        .with_graceful_shutdown(shutdown_signal())
        .await?;

    Ok(())
}

async fn shutdown_signal() {
    let ctrl_c = async {
        signal::ctrl_c()
            .await
            .expect("failed to install Ctrl+C handler");
        info!("Received Ctrl+C signal, starting shutdown");
    };

    #[cfg(unix)]
    let terminate = async {
        signal::unix::signal(signal::unix::SignalKind::terminate())
            .expect("failed to install signal handler")
            .recv()
            .await;
        info!("Received terminate signal, starting shutdown");
    };

    #[cfg(not(unix))]
    let terminate = std::future::pending::<()>();

    tokio::select! {
        _ = ctrl_c => {},
        _ = terminate => {},
    }
}
